if (typeof jQuery == 'undefined') {
    alert("need jquery");
} if(typeof Strophe == 'undefined'){
    alert("need Strophe");
} else {

(function($) {
if (typeof Easemob == 'undefined') {
    Easemob = {};
}
if (typeof Easemob.im == 'undefined') {
    Easemob.im = {};
    Easemob.im.version="1.0.7";
}
if (typeof Easemob.im.Connection !== 'undefined') {
    return;
}

var innerBase64 = (function() {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    var obj = {
        /**
         * Encodes a string in base64
         *
         * @param {String}
         *            input The string to encode in base64.
         */
        encode : function(input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            do {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2)
                        + keyStr.charAt(enc3) + keyStr.charAt(enc4);
            } while (i < input.length);

            return output;
        },

        byteEncode : function(bytes) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            do {
                chr1 = bytes[i++];
                chr2 = bytes[i++];
                chr3 = bytes[i++];

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2)
                        + keyStr.charAt(enc3) + keyStr.charAt(enc4);
            } while (i < bytes.length);

            return output;
        },

        /**
         * Decodes a base64 string.
         *
         * @param {String}
         *            input The string to decode.
         */
        decode : function(input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            do {
                enc1 = keyStr.indexOf(input.charAt(i++));
                enc2 = keyStr.indexOf(input.charAt(i++));
                enc3 = keyStr.indexOf(input.charAt(i++));
                enc4 = keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }
            } while (i < input.length);

            return output;
        }
    };

    return obj;
})();

var emptyFn = function() {};

var tempIndex = 0;
EASEMOB_IM_CONNCTION_USER_NOT_ASSIGN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_OPEN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_AUTH_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_ATTACH_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_ATTACH_USERGRID_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_REOPEN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_SERVER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_IQ_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_PING_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_NOTIFYVERSION_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROSTER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_CROSSDOMAIN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_LISTENING_OUTOF_MAXRETRIES = tempIndex++;
EASEMOB_IM_CONNCTION_RECEIVEMSG_CONTENTERROR = tempIndex++;
EASEMOB_IM_CONNCTION_JOINROOM_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOM_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMINFO_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMMEMBER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMOCCUPANTS_ERROR = tempIndex++;

EASEMOB_IM_UPLOADFILE_BROWSER_ERROR = tempIndex++;
EASEMOB_IM_UPLOADFILE_ERROR = tempIndex++;
EASEMOB_IM_UPLOADFILE_NO_LOGIN = tempIndex++;
EASEMOB_IM_UPLOADFILE_NO_FILE = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_ERROR = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_NO_LOGIN = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_BROWSER_ERROR = tempIndex++;

EASEMOB_IM_RESISTERUSER_ERROR = tempIndex++;

tempIndex = 0;
EASEMOB_IM_MESSAGE_REC_TEXT = tempIndex++;
EASEMOB_IM_MESSAGE_REC_TEXT_ERROR = tempIndex++;
EASEMOB_IM_MESSAGE_REC_EMOTION = tempIndex++;
EASEMOB_IM_MESSAGE_REC_PHOTO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_AUDIO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_AUDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_REC_VEDIO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_VEDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_REC_FILE = tempIndex++;

EASEMOB_IM_MESSAGE_SED_TEXT = tempIndex++;
EASEMOB_IM_MESSAGE_SED_EMOTION = tempIndex++;
EASEMOB_IM_MESSAGE_SED_PHOTO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_AUDIO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_AUDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_SED_VEDIO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_VEDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_SED_FILE = tempIndex++;
EASEMOB_IM_FILESIZE_LIMIT = 10485760;
var emotionPicData = {
        "[):]":"",
        "[:D]":"",
        "[;)]":"",
        "[:-o]":"",
        "[:p]":"",
        "[(H)]":"",
        "[:@]":"",
        "[:s]":"",
        "[:$]":"",
        "[:(]":"",
        "[:'(]":"",
        "[:|]":"",
        "[(a)]":"",
        "[8o|]":"",
        "[8-|]":"",
        "[+o(]":"",
        "[<o)]":"",
        "[|-)]":"",
        "[*-)]":"",
        "[:-#]":"",
        "[:-*]":"",
        "[^o)]":"",
        "[8-)]":"",
        "[(|)]":"",
        "[(u)]":"",
        "[(S)]":"",
        "[(*)]":"",
        "[(#)]":"",
        "[(R)]":"",
        "[({)]":"",
        "[(})]":"",
        "[(k)]":"",
        "[(F)]":"",
        "[(W)]":"",
        "[(D)]":""
};
var createStandardXHR = function () {
    try {
        return new window.XMLHttpRequest();
    } catch( e ) {
        return false;
    }
};
var createActiveXHR = function () {
    try {
        return new window.ActiveXObject( "Microsoft.XMLHTTP" );
    } catch( e ) {
        return false;
    }
};
if (window.XDomainRequest) {
    XDomainRequest.prototype.oldsend = XDomainRequest.prototype.send;
    XDomainRequest.prototype.send = function() {
        XDomainRequest.prototype.oldsend.apply(this, arguments);
        this.readyState = 2;
    };
}

var xmlrequest = function (crossDomain){
    crossDomain = crossDomain || true;
    var temp = createStandardXHR () || createActiveXHR();

    if ("withCredentials" in temp) {
        return temp;
    }
    if(!crossDomain){
        return temp;
    }
    if(window.XDomainRequest===undefined){
        return temp;
    }
    var xhr = new XDomainRequest();
    xhr.readyState = 0;
    xhr.status = 100;
    xhr.onreadystatechange = emptyFn;
    xhr.onload = function () {
        xhr.readyState = 4;
        xhr.status = 200;

        var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async = "false";
        xmlDoc.loadXML(xhr.responseText);
        xhr.responseXML = xmlDoc;
        xhr.response = xhr.responseText;
        xhr.onreadystatechange();
    };
    xhr.ontimeout = xhr.onerror = function(){
        xhr.readyState = 4;
        xhr.status = 500;
        xhr.onreadystatechange();
    };
    return xhr;
};
Strophe.Request.prototype._newXHR = function(){
    var xhr =  xmlrequest(true);
  if (xhr.overrideMimeType) {
      xhr.overrideMimeType("text/xml");
  }
  xhr.onreadystatechange = this.func.bind(null, this);
  return xhr;
};

function getIEVersion(){
    var ua = navigator.userAgent,matches,tridentMap={'4':8,'5':9,'6':10,'7':11};
    matches = ua.match(/MSIE (\d+)/i);
    if(matches&&matches[1])
    {
        return +matches[1];
    }
    matches = ua.match(/Trident\/(\d+)/i);
    if(matches&&matches[1])
    {
        return tridentMap[matches[1]]||null;
    }
    return null;
};

var tepmxhr = xmlrequest();
var hasSetRequestHeader = (tepmxhr.setRequestHeader || false );
var hasOverrideMimeType = (tepmxhr.overrideMimeType || false);
tepmxhr = null;

var doAjaxRequest = function(options) {
    var dataType = options.dataType || 'text';
    var suc = options.success || emptyFn;
    var error = options.error || emptyFn;
    var xhr = xmlrequest();
    xhr.onreadystatechange = function (){
        if( xhr.readyState === 4){
            var status = xhr.status || 0;
            if (status == 200) {
                if(dataType=='text'){
                    suc(xhr.responseText,xhr);
                    return;
                }
                if(dataType=='json'){
                    try{
                        var json = $.parseJSON(xhr.responseText);
                        suc(json,xhr);
                    } catch(e){
                        error(xhr.responseText,xhr,"错误的数据,无法转换为json");
                    }
                    return;
                }
                if(dataType=='xml'){
                    if (xhr.responseXML && xhr.responseXML.documentElement) {
                        suc(xhr.responseXML.documentElement,xhr);
                    } else {
                        error(xhr.responseText,xhr,"浏览器不支持ajax返回xml对象");
                    }
                    return;
                }
                suc(xhr.response || xhr.responseText,xhr);
                return;
            } else {
                if(dataType=='json'){
                    try{
                        var json = $.parseJSON(xhr.responseText);
                        error(json,xhr,"服务器返回错误信息");
                    } catch(e){
                        error(xhr.responseText,xhr,"服务器返回错误信息");
                    }
                    return;
                }
                if(dataType=='xml'){
                    if (xhr.responseXML && xhr.responseXML.documentElement) {
                        error(xhr.responseXML.documentElement,xhr,"服务器返回错误信息");
                    } else {
                        error(xhr.responseText,xhr,"服务器返回错误信息");
                    }
                    return;
                }
                error(xhr.responseText,xhr,"服务器返回错误信息");
                return;
            }
        }
        if( xhr.readyState === 0){
            error(xhr.responseText,xhr,"服务器异常");
        }
    };

    if(options.responseType){
        if(xhr.responseType){
            xhr.responseType = options.responseType;
        } else {
            error('',xhr,"当前浏览器不支持设置响应类型");
            return null;
        }
    }
    if(options.mimeType){
        if(hasOverrideMimeType){
            xhr.overrideMimeType(options.mimeType);
        } else {
            error('',xhr,"当前浏览器不支持设置mimeType");
            return null;
        }
    }

    var type = options.type || "POST";
    xhr.open(type, options.url);

    var headers = options.headers || {};
    for(var key in headers){
        if(hasSetRequestHeader){
            xhr.setRequestHeader(key, headers[key]);
        } else {
            error('',xhr,"当前浏览器不支持设置header");
            return null;
        }
    }

    var data = options.data || null;
    xhr.send(data);
    return xhr;
};

var registerUserFn = function(options){
    var orgName = options.orgName || '';
    var appName = options.appName || '';
    var appKey = options.appKey || '';
    if(!orgName && !appName && appKey){
        var devInfos = appKey.split('#');
        if(devInfos.length==2){
            orgName = devInfos[0];
            appName = devInfos[1];
        }
    }
    if(!orgName && !appName){
        options.error({
            type : EASEMOB_IM_RESISTERUSER_ERROR,
            msg : '没有指定开发者信息'
        });
        return;
    }

    var url = options.https ? 'https://a1.easemob.com' : 'http://a1.easemob.com';
    var apiUrl = options.apiUrl || url;
    var restUrl = apiUrl + '/' + orgName + '/' + appName + '/users';

    var userjson = {
            username : options.username,
            password : options.password,
            nickname : options.nickname || ''
    };

    var userinfo = JSON.stringify(userjson);
    var options = {
        url : restUrl,
        dataType : 'json',
        data : userinfo,
        success : options.success || emptyFn,
        error : options.error || emptyFn
    };
    var param = doAjaxRequest(options);
    return param;
};


var getFileUrlFn = function(fileInputId) {
    var uri = {
        url : '',
        filename : '',
        filetype : ''
    };

    if(!isCanUploadFileAsync) return uri;
    var wu = window.URL || window.webkitURL || window.mozURL || window.msURL;
    if (wu && wu.createObjectURL) {
        var fileItems = document.getElementById(fileInputId).files;
        if (fileItems.length > 0) {
            var u = fileItems.item(0);
            uri.url = wu.createObjectURL(u);
            uri.filename = u.name || '';
        }
    } else { // IE
        var u = document.getElementById(fileInputId).value;
        uri.url = u;
        var pos1 = u.lastIndexOf('/');
        var pos2 = u.lastIndexOf('\\');
        var pos = Math.max(pos1, pos2)
        if (pos < 0)
            uri.filename = u;
        else
            uri.filename = u.substring(pos + 1);
    }
    var index = uri.filename.lastIndexOf(".");
    if (index != -1) {
        uri.filetype = uri.filename.substring(index+1).toLowerCase();
    }
    return uri;
};
var isIe = false;
if (!!window.ActiveXObject || "ActiveXObject" in window) {
    isIe = true;
}
var getFileSizeFn = function(fileInputId){
    var file = document.getElementById(fileInputId)
    var fileSize = 0;
    if(file){
        if(file.files){
            if(file.files.length>0){
                fileSize = file.files[0].size;
            }
        } else if(isIe){
            file.select();
            var fileobject = new ActiveXObject ("Scripting.FileSystemObject");
            var file = fileobject.GetFile (file.value);
            fileSize = file.Size;
        }
    }
    return fileSize;
};

var hasFlash = (function() {
    if (/*@cc_on!@*/0) {//ie
        return new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
    } else {
        if (navigator.plugins && navigator.plugins.length > 0) {
            return navigator.plugins["Shockwave Flash"];
        }
    }
    return 0;
}());

var hasFormData = (typeof FormData != 'undefined');
var isCanUploadFileAsync = hasSetRequestHeader && hasFormData;
var isCanUploadFile = isCanUploadFileAsync || hasFlash;
var uploadUrl = '';
var parseUploadResponse = function(response) {
    return response.indexOf('callback') > -1 ? //lte ie9
        response.slice(9, -1) : response;
}
var parseDownloadResponse = function(response) {
    return ((response && response.type && response.type === 'application/json') 
        || 0 > Object.prototype.toString.call(response).indexOf('Blob')) ? 
            this.url+'?token=' : window.URL.createObjectURL(response);
}
var uploadFn = function(options) {
    options = options || {};
    options.onFileUploadProgress = options.onFileUploadProgress || emptyFn;
    options.onFileUploadComplete = options.onFileUploadComplete || emptyFn;
    options.onFileUploadError = options.onFileUploadError || emptyFn;
    options.onFileUploadCanceled = options.onFileUploadCanceled || emptyFn;
    var acc = options.accessToken;
    if (!acc) {
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_NO_LOGIN,
            msg : '用户未登录到usergrid服务器,无法使用文件上传功能'
        });
        return;
    }

    orgName = options.orgName || '';
    appName = options.appName || '';
    appKey = options.appKey || '';
    if(!orgName && !appName && appKey){
        var devInfos = appKey.split('#');
        if(devInfos.length==2){
            orgName = devInfos[0];
            appName = devInfos[1];
        }
    }
    if(!orgName && !appName){
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_ERROR,
            msg : '没有指定开发者信息'
        });
        return;
    }
    var apiUrl = options.apiUrl || 'http://a1.easemob.com';
    var uploadUrl = apiUrl + '/' + orgName + '/' + appName + '/chatfiles';
    if (!isCanUploadFileAsync) {
		if(hasFlash && typeof options.flashUpload === 'function') {
            options.flashUpload && options.flashUpload(uploadUrl, options); 
        } else {
            this.onError({
                type : EASEMOB_IM_UPLOADFILE_BROWSER_ERROR,
                msg : '当前浏览器不支持异步上传！'
            });
        }
        return;
    }

    
    var fileSize = getFileSizeFn(options.fileInputId);
    if(fileSize > EASEMOB_IM_FILESIZE_LIMIT){
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_ERROR,
            msg : '上传文件超过服务器大小限制（10M）'
        });
        return ;
    }else if(fileSize <= 0){
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_ERROR,
            msg : '上传文件大小为0'
        });
        return ;
    }

    var xhr = xmlrequest();
    var onError = function(e) {
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_ERROR,
            msg : '上传文件失败',
            e : e,
            xhr : xhr
        });
    }
    if(xhr.upload){
        xhr.upload.addEventListener("progress",options.onFileUploadProgress, false);
    }
    if(xhr.addEventListener){
        xhr.addEventListener("abort", options.onFileUploadCanceled, false);
        xhr.addEventListener("load", function(e) {
            try{
                var json = $.parseJSON(xhr.responseText);
                options.onFileUploadComplete(json);
            } catch(e){
                options.onFileUploadError({
                    type : EASEMOB_IM_UPLOADFILE_ERROR,
                    msg : '上传文件失败,服务端返回值值不正确',
                    e : e,
                    data : xhr.responseText,
                    xhr : xhr
                });
            }
        }, false);
        xhr.addEventListener("error", onError, false);
    } else if(xhr.onreadystatechange){
        xhr.onreadystatechange = function (){
            if( xhr.readyState === 4){
                if (ajax.status == 200) {
                    try{
                        var json = $.parseJSON(xhr.responseText);
                        options.onFileUploadComplete(json);
                    } catch(e){
                        options.onFileUploadError({
                            type : EASEMOB_IM_UPLOADFILE_ERROR,
                            msg : '上传文件失败,服务端返回值不正确',
                            e : e,
                            data : xhr.responseText,
                            xhr : xhr
                        });
                    }
                } else {
                        options.onFileUploadError({
                            type : EASEMOB_IM_UPLOADFILE_ERROR,
                            msg : '上传文件失败,服务端返回异常',
                            data : xhr.responseText,
                            xhr : xhr
                        });
                }
            } else {
                xhr.abort();
                options.onFileUploadCanceled();
            }
        }
    }

    xhr.open("POST", uploadUrl);

    xhr.setRequestHeader('restrict-access', 'true');
    xhr.setRequestHeader('Authorization', 'Bearer ' + acc);

    var localFile = '';
    var fileInput = document.getElementById(options.fileInputId);
    var localFile = null;
    if ("files" in fileInput) {
        localFile = fileInput.files[0];
    } else {
        localFile = fileInput.value;
    }
    var formData = new FormData();
    formData.append("file", localFile);
    xhr.send(formData);
};
var hasBlob = (typeof Blob != 'undefined');
var isCanDownLoadFile = (hasSetRequestHeader && (hasBlob || hasOverrideMimeType));
var downloadFn = function(options){
    options.onFileDownloadComplete = options.onFileDownloadComplete || emptyFn;
    options.onFileDownloadError = options.onFileDownloadError || emptyFn;
    
    var accessToken = options.accessToken || '';
    if (!accessToken) {
        options.onFileDownloadError({
            type : EASEMOB_IM_DOWNLOADFILE_NO_LOGIN,
            msg : '用户未登录到usergrid服务器,无法使用文件下载功能'
        });
        return;
    }

    var onError = function(e) {
        options.onFileDownloadError({
            type : EASEMOB_IM_DOWNLOADFILE_ERROR,
            msg : '下载文件失败',
            xhr : xhr,
            e : e
        });
    }
    if (!isCanDownLoadFile) {
        options.onFileDownloadComplete();
        return;
    }
    var xhr = xmlrequest();
    if("addEventListener" in xhr){
        xhr.addEventListener("load", function(e) {
            options.onFileDownloadComplete(xhr.response,xhr);
        }, false);
        xhr.addEventListener("error", onError, false);
    } else if("onreadystatechange" in xhr){
        xhr.onreadystatechange = function (){
            if( xhr.readyState === 4){
                if (ajax.status == 200) {
                    options.onFileDownloadComplete(xhr.response,xhr);
                } else {
                        options.onFileDownloadError({
                            type : EASEMOB_IM_DOWNLOADFILE_ERROR,
                            msg : '下载文件失败,服务端返回异常',
                            xhr : xhr
                        });
                }
            } else {
                xhr.abort();
                options.onFileDownloadError({
                    type : EASEMOB_IM_DOWNLOADFILE_ERROR,
                    msg : '错误的下载状态,退出下载',
                    xhr : xhr
                });
            }
        }
    }

    var method = options.method || 'GET';
    var resType = options.responseType || 'blob';
    var mimeType = options.mimeType || "text/plain; charset=x-user-defined";
    xhr.open(method, options.url);
    if(typeof Blob != 'undefined'){
        xhr.responseType = resType;
    } else {
        xhr.overrideMimeType(mimeType);
    }

    var innerHeaer = {
        'X-Requested-With' : 'XMLHttpRequest',
        'Accept' : 'application/octet-stream',
        'share-secret' : options.secret,
        'Authorization' : 'Bearer ' + accessToken
    };
    var headers = options.headers || {};
    for(var key in headers){
        innerHeaer[key] = headers[key];
    }
    for(var key in innerHeaer){
        if(innerHeaer[key]){
            xhr.setRequestHeader(key, innerHeaer[key]);
        }
    }
    xhr.send(null);
};

var parseNameFromJidFn = function(jid,domain){
    domain = domain || "";
    var tempstr = jid;
    var findex = tempstr.indexOf("_");
    if(findex!=-1){
        tempstr = tempstr.substring(findex+1);
    }
    var atindex = tempstr.indexOf("@" + domain);
    if(atindex!=-1){
        tempstr = tempstr.substring(0,atindex);
    }
    return tempstr;
};

var parseTextMessageFn = function(message){
    if(typeof(message) != 'string'){
        conn.onError({
            type : EASEMOB_IM_MESSAGE_REC_TEXT_ERROR,
            msg : '不合法的消息内容格式，请检查发送消息内容！'
        });
        return;
    }
    var receiveMsg = message;
    var emessage = [];
    var expr = /\[[^[\]]{2,3}\]/mg;
    var emotions = receiveMsg.match(expr);
    if (!emotions || emotions.length < 1){
        return {"isemotion":false,"body":[{"type" : "txt","data":message}]};
    }
    var isemotion = false;
    for (var i = 0; i < emotions.length; i++) {
        var tmsg = receiveMsg.substring(0,receiveMsg.indexOf(emotions[i]));
        if (tmsg) {
            emessage.push({
                "type" : "txt",
                "data" : tmsg
            });
        }
        var emotion = emotionPicData[emotions[i]];
        if (emotion) {
            isemotion = true;
            emessage.push({
                "type" : "emotion",
                "data" : emotion
            });
        } else {
            emessage.push({
                "type" : "txt",
                "data" : emotions[i]
            });
        }
        var restMsgIndex = receiveMsg.indexOf(emotions[i]) + emotions[i].length;
        receiveMsg = receiveMsg.substring(restMsgIndex);
    }
    if (receiveMsg) {
        emessage.push({
            "type" : "txt",
            "data" : receiveMsg
        });
    }
    if(isemotion){
        return {"isemotion":isemotion,"body":emessage};
    }
    return {"isemotion":false,"body":[{"type" : "txt","data":message}]};
}

var parseResponseMessageFn = function(msginfo){
    var parseMsgData = {errorMsg:true,data:[]};

    var msgBodies = msginfo.getElementsByTagName("body");
    if(msgBodies){
        for (var i=0;i<msgBodies.length;i++){
            var msgBody = msgBodies[i];
            var childNodes = msgBody.childNodes;
            if(childNodes && childNodes.length>0){
                var childNode = msgBody.childNodes[0];
                if(childNode.nodeType==Strophe.ElementType.TEXT){
                    var jsondata = childNode.wholeText ||childNode.nodeValue;
                    jsondata = jsondata.replace('\n','<br>');
                    try{
                        var data = eval("("+jsondata+")");
                        parseMsgData.errorMsg = false;
                        parseMsgData.data = [data];
                    }catch(e){
                    }
                }
            }
        }
        var delayTags = msginfo.getElementsByTagName("delay");
        if(delayTags && delayTags.length>0){
            var delayTag = delayTags[0];
            var delayMsgTime = delayTag.getAttribute("stamp");
            if(delayMsgTime){
                parseMsgData.delayTimeStamp = delayMsgTime;
            }
        }
    } else {
        var childrens = msginfo.childNodes;
        if(childrens&&childrens.length>0){
            var child = msginfo.childNodes[0];
            if(child.nodeType==Strophe.ElementType.TEXT){
                try{
                    var data = eval("("+child.nodeValue+")");
                    parseMsgData.errorMsg = false;
                    parseMsgData.data = [data];
                } catch(e){
                }

            }
        }
    }
    return parseMsgData;
};
var parseFriendFn = function(queryTag){
    var rouster = [];
    var items = queryTag.getElementsByTagName("item");
    if(items){
        for(var i=0;i<items.length;i++){
            var item = items[i];
            var jid = item.getAttribute('jid');
            if(!jid){
                continue;
            }
            var subscription = item.getAttribute('subscription');
            var friend = {
                        subscription : subscription,
                        jid : jid
            };
            var ask = item.getAttribute('ask');
            if(ask){
                friend.ask = ask;
            }
            var name = item.getAttribute('name');
            if(name){
                friend.name = name;
            } else {
                var n = parseNameFromJidFn(jid);
                friend.name = n;
            }
            var groups = [];
            Strophe.forEachChild(item, 'group',function(group){
                groups.push(Strophe.getText(group));
            });
            friend.groups = groups;
            rouster.push(friend);
        }
    }
    return rouster;
};
var parseRoomFn = function(result){
    var rooms = [];
    var items = result.getElementsByTagName("item");
    if(items){
        for(var i=0;i<items.length;i++){
            var item = items[i];
            var roomJid = item.getAttribute('jid');
            var tmp = roomJid.split("@")[0];
            var room = {
                    jid : roomJid,
                    name : item.getAttribute('name'),
                    roomId : tmp.split('_')[1]
                };
            rooms.push(room);
        }
    }
    return rooms;
};
var parseRoomOccupantsFn = function(result){
    var occupants = [];
    var items = result.getElementsByTagName("item");
    if(items){
        for(var i=0;i<items.length;i++){
            var item = items[i];
            var room = {
                    jid : item.getAttribute('jid'),
                    name : item.getAttribute('name')
                };
            occupants.push(room);
        }
    }
    return occupants;
}
var login2UserGrid = function(options){
    options = options || {};

    var appKey = options.appKey || '';
    var devInfos = appKey.split('#');
    if(devInfos.length!=2){
        error({
            type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    var orgName = devInfos[0];
    var appName = devInfos[1];
    if(!orgName){
        error({
            type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    if(!appName){
        error({
            type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    var suc = options.success || emptyFn;
    var error = options.error || emptyFn;
    var user = options.user || '';
    var pwd = options.pwd || '';

    var https = options.https;
    var url = https ? 'https://a1.easemob.com' : 'http://a1.easemob.com';
    var apiUrl = options.apiUrl || url;

    return dologin2UserGrid(apiUrl,user,pwd,orgName,appName,suc,error);
};
var dologin2UserGrid = function(apiUrl,user,pwd,orgName,appName,suc,error) {
    var loginJson = {
        grant_type : 'password',
        username : user,
        password : pwd
    };
    var loginfo = JSON.stringify(loginJson);

    var options = {
        url : apiUrl+"/"+orgName+"/"+appName+"/token",
        dataType : 'json',
        data : loginfo,
        success : suc || emptyFn,
        error : error || emptyFn
    };
    var param = doAjaxRequest(options);
    return param;
};

var innerCheck = function(options,conn){
    if (conn.isOpened() || conn.isOpening()) {
        conn.onError({
            type : EASEMOB_IM_CONNCTION_REOPEN_ERROR,
            msg : '重复打开连接,请先关闭连接再打开'
        });
        return false;
    }
    options = options || {};

    var user = options.user || '';
    if (options.user == '') {
        conn.onError({
            type : EASEMOB_IM_CONNCTION_USER_NOT_ASSIGN_ERROR,
            msg : '未指定用户'
        });
        return false;
    }

    var appKey = options.appKey || "";
    var devInfos = appKey.split('#');
    if(devInfos.length!=2){
        conn.onError({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    var orgName = devInfos[0];
    var appName = devInfos[1];
    if(!orgName){
        conn.onError({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    if(!appName){
        conn.onError({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '请指定正确的开发者信息(appKey)'
        });
        return false;
    }
    
    // jid = {appkey}_{username}@domain/resource
    var jid = appKey + "_" + user + "@" + conn.domain;

    var resource_value = Math.floor(Math.random()*1000);
    //var resource_value = "webim";
    
    var resource = options.resource || resource_value;
    if(resource != ""){
        jid = jid + "/" + resource;
    }
    conn.context.jid = jid;
    conn.context.userId = user;
    conn.context.appKey = appKey;
    conn.context.appName = appName;
    conn.context.orgName = orgName;
    
    return true;
};
var parseMessageType = function(msginfo){
    var msgtype = 'normal';
    var receiveinfo = msginfo.getElementsByTagName("received");
    if(receiveinfo && receiveinfo.length > 0 && receiveinfo[0].namespaceURI == "urn:xmpp:receipts"){
        msgtype = 'received';
    }else{
        var inviteinfo =  msginfo.getElementsByTagName("invite");
        if(inviteinfo && inviteinfo.length > 0){
            msgtype = 'invite';
        }
    }
    return msgtype;
};
var login2ImCallback = function (status,msg,conn){
    if (status == Strophe.Status.CONNFAIL){
      conn.onError({
            type : EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR,
            msg : msg
        });
    } else if ((status == Strophe.Status.ATTACHED) || (status == Strophe.Status.CONNECTED)){
        var handleMessage = function(msginfo){
            var type = parseMessageType(msginfo);
            if('received' == type){
                conn.handleReceivedMessage(msginfo);
                return true;
            }else if('invite' == type){
                conn.handleInviteMessage(msginfo);
                return true;
            }else{
                conn.handleMessage(msginfo);
                return true;
            }
        };
        var handlePresence = function(msginfo){
            conn.handlePresence(msginfo);
            return true;
        };
        var handlePing = function(msginfo){
            conn.handlePing(msginfo);
            return true;
        };
        var handleIq = function(msginfo){
            conn.handleIq(msginfo);
            return true;
        };

        conn.addHandler(handleMessage, null, 'message', null, null,  null);
        conn.addHandler(handlePresence, null, 'presence', null, null,  null);
        conn.addHandler(handlePing, "urn:xmpp:ping", 'iq', "get", null,  null);
        conn.addHandler(handleIq, "jabber:iq:roster", 'iq', "set", null,  null);

        conn.context.status = STATUS_OPENED;
        var supportRecMessage = [
           EASEMOB_IM_MESSAGE_REC_TEXT,
           EASEMOB_IM_MESSAGE_REC_EMOTION ];
        if (isCanDownLoadFile) {
            supportRecMessage.push(EASEMOB_IM_MESSAGE_REC_PHOTO);
            supportRecMessage.push(EASEMOB_IM_MESSAGE_REC_AUDIO_FILE);
        }
        var supportSedMessage = [ EASEMOB_IM_MESSAGE_SED_TEXT ];
        if (isCanUploadFile) {
            supportSedMessage.push(EASEMOB_IM_MESSAGE_REC_PHOTO);
            supportSedMessage.push(EASEMOB_IM_MESSAGE_REC_AUDIO_FILE);
        }
        conn.notifyVersion();
        conn.onOpened({
            canReceive : supportRecMessage,
            canSend : supportSedMessage,
            accessToken : conn.context.accessToken
        });
    } else if (status == Strophe.Status.DISCONNECTING) {
        if(conn.isOpened()){// 不是主动关闭
            conn.context.status = STATUS_CLOSING;
            conn.onError({
                type : EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR,
                msg : msg
            });
        }
    } else if (status == Strophe.Status.DISCONNECTED) {
        conn.context.status = STATUS_CLOSED;
        conn.clear();
        conn.onClosed();
    } else if (status == Strophe.Status.AUTHFAIL){
        conn.onError({
            type : EASEMOB_IM_CONNCTION_AUTH_ERROR,
            msg : '登录失败,请输入正确的用户名和密码'
        });
        conn.clear();
    } else if(status == Strophe.Status.ERROR){
        conn.onError({
            type : EASEMOB_IM_CONNCTION_SERVER_ERROR,
            msg : msg || '服务器异常'
        });
    }
};
var getJid = function(options,conn){
    var jid = options.toJid || '';
    if(jid==''){
        var appKey = conn.context.appKey || '';
        var toJid = appKey + "_" + options.to + "@"
                + conn.domain;
        if(options.resource){
            toJid = toJid + "/" + options.resource;
        }
        jid = toJid;
    }
    return jid;
};

tempIndex = 0;
var STATUS_INIT = tempIndex++;
var STATUS_DOLOGIN_USERGRID = tempIndex++;
var STATUS_DOLOGIN_IM = tempIndex++;
var STATUS_OPENED = tempIndex++;
var STATUS_CLOSING = tempIndex++;
var STATUS_CLOSED = tempIndex++;

var connection = function() {
}
connection.prototype.init = function(options) {
    if (window.WebSocket) {
        this.url = options.url || (options.https ? 'wss' : 'ws') + '://im-api.easemob.com/ws/';
    } else {
        this.url = ((options.url && options.url.indexOf('ws') > -1) ? '' : options.url) || (options.https ? 'https' : 'http') + '://im-api.easemob.com/http-bind/';
    }

    this.https = options.https || false;
    this.wait = options.wait || 30;
    this.hold = options.hold || 1;
    if(options.route){
        this.route = options.route;
    }

    this.domain = options.domain || "easemob.com";
    this.inactivity = options.inactivity || 30;
    this.maxRetries = options.maxRetries || 5;
    this.pollingTime = options.pollingTime || 800;
    this.stropheConn = false;
    this.onOpened = options.onOpened || emptyFn;
    this.onClosed = options.onClosed || emptyFn;
    this.onTextMessage = options.onTextMessage || emptyFn;
    this.onEmotionMessage = options.onEmotionMessage || emptyFn;
    this.onPictureMessage = options.onPictureMessage || emptyFn;
    this.onAudioMessage = options.onAudioMessage || emptyFn;
    this.onVideoMessage = options.onVideoMessage || emptyFn;
    this.onFileMessage = options.onFileMessage || emptyFn;
    this.onLocationMessage = options.onLocationMessage || emptyFn;
    this.onCmdMessage = options.onCmdMessage || emptyFn;
    this.onPresence = options.onPresence || emptyFn;
    this.onRoster = options.onRoster || emptyFn;
    this.onError = options.onError || emptyFn;
    this.onReceivedMessage = options.onReceivedMessage || emptyFn;
    this.onInviteMessage = options.onInviteMessage || emptyFn;

    this.context = {
        status : STATUS_INIT
    };
}
var dologin2IM = function(options,conn){
    var accessToken = options.access_token || '';
    if(accessToken == ''){
        var loginfo = JSON.stringify(options);
        conn.onError({
            type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
            msg : "登录失败,"+ loginfo,
            data : options,
            xhr : xhr
        });
        return;
    }
    conn.context.accessToken = options.access_token;
    conn.context.accessTokenExpires = options.expires_in;
    var stropheConn = conn.context.stropheConn || new Strophe.Connection(conn.url,{
                        inactivity : conn.inactivity,
                        maxRetries : conn.maxRetries,
                        pollingTime : conn.pollingTime
    });
    var callback = function(status,msg){
        login2ImCallback(status,msg,conn);
    };
    var jid = conn.context.jid;
    conn.context.stropheConn = stropheConn;
    if(conn.route){
        stropheConn.connect(jid,"$t$" + accessToken,callback,conn.wait,conn.hold,conn.route);
    } else {
        stropheConn.connect(jid,"$t$" + accessToken,callback,conn.wait,conn.hold);
    }

};
// user, pwd, appKey, resource
connection.prototype.open = function(options) {
    var pass = innerCheck(options,this);
    if(pass == false){
        return;
    }
    
    var conn = this;
    if(options.accessToken){
        options.access_token = options.accessToken;
        dologin2IM(options,conn);
    }else{
        var loginUrl = this.https ? "https://a1.easemob.com" : "http://a1.easemob.com";
        var apiUrl = options.apiUrl || loginUrl;
        var userId = this.context.userId;
        var pwd = options.pwd || '';
        var appName = this.context.appName;
        var orgName = this.context.orgName;

        var suc = function(data,xhr){
            conn.context.status = STATUS_DOLOGIN_IM;
            dologin2IM(data,conn);
        };
        var error = function(res,xhr,msg){
            if(res.error && res.error_description){
                conn.onError({
                    type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
                    msg : "登录失败,"+res.error_description,
                    data : res,
                    xhr : xhr
                });
            } else {
                conn.onError({
                    type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
                    msg : "登录失败",
                    data : res,
                    xhr : xhr
                });
            }
            conn.clear();
        };
        this.context.status = STATUS_DOLOGIN_USERGRID;
        dologin2UserGrid(apiUrl,userId,pwd,orgName,appName,suc,error);
    }

};
connection.prototype.attach = function(options) {
    var pass = innerCheck(options,this);
    if(pass == false)
        return;{
    }
    options = options || {};

    var accessToken = options.accessToken || '';
    if(accessToken == ''){
        this.onError({
            type : EASEMOB_IM_CONNCTION_ATTACH_USERGRID_ERROR,
            msg : '未指定用户的accessToken'
        });
        return;
    }

    var sid = options.sid || '';
    if(sid == ''){
        this.onError({
            type : EASEMOB_IM_CONNCTION_ATTACH_ERROR,
            msg : '未指定用户的会话信息'
        });
        return;
    }

    var rid = options.rid || '';
    if(rid == ''){
        this.onError({
            type : EASEMOB_IM_CONNCTION_ATTACH_ERROR,
            msg : '未指定用户的消息id'
        });
        return;
    }

    var stropheConn = new Strophe.Connection(this.url,{
                        inactivity : this.inactivity,
                        maxRetries : this.maxRetries,
                        pollingTime : this.pollingTime
    });

    this.context.accessToken = accessToken;
    this.context.stropheConn = stropheConn;
    this.context.status = STATUS_DOLOGIN_IM;

    var conn = this;
    var callback = function(status,msg){
        login2ImCallback(status,msg,conn);
    };
    var jid = this.context.jid;
    var wait = this.wait;
    var hold = this.hold;
    var wind = this.wind || 5;
    stropheConn.attach(jid, sid, rid, callback, wait, hold, wind);
};
connection.prototype.close = function() {
    var status = this.context.status;
    if (status==STATUS_INIT) {
        return;
    }
    if(this.isClosed() || this.isClosing()){
        return;
    }
    this.context.status = STATUS_CLOSING;
    this.context.stropheConn.disconnect();
};
// see stropheConn.addHandler
connection.prototype.addHandler = function (handler, ns, name, type, id, from, options){
    this.context.stropheConn.addHandler(handler, ns, name, type, id, from, options);
};
connection.prototype.notifyVersion = function (suc,fail){
    var jid = getJid({},this);
    var dom = $iq({
            from : this.context.jid || '',
            to: this.domain,
            type: "result"
    }).c("query", {xmlns: "jabber:iq:version"}).c("name").t("easemob").up().c("version").t(Easemob.im.version).up().c("os").t("webim");
    suc = suc || emptyFn;
    error = fail || this.onError;
    var failFn = function(ele){
        error({
            type : EASEMOB_IM_CONNCTION_NOTIFYVERSION_ERROR,
            msg : '发送版本信息给服务器时失败',
            data : ele
        });
    };
    this.context.stropheConn.sendIQ(dom.tree(),suc,failFn);
    return;
};
connection.prototype.handlePresence = function(msginfo){
    if(this.isClosed()){
        return;
    }
    var from = msginfo.getAttribute('from') || '';
    var to = msginfo.getAttribute('to') || '';
    var type = msginfo.getAttribute('type') || '';
    var fromUser = parseNameFromJidFn(from);
    var toUser = parseNameFromJidFn(to);
    var info = {
        from: fromUser,
        to : toUser,
        fromJid : from,
        toJid : to,
        type : type
    };

    var showTags = msginfo.getElementsByTagName("show");
    if(showTags && showTags.length>0){
        var showTag = showTags[0];
        info.show = Strophe.getText(showTag);
    }
    var statusTags = msginfo.getElementsByTagName("status");
    if(statusTags && statusTags.length>0){
        var statusTag = statusTags[0];
        info.status = Strophe.getText(statusTag);
    }

    var priorityTags = msginfo.getElementsByTagName("priority");
    if(priorityTags && priorityTags.length>0){
        var priorityTag = priorityTags[0];
        info.priority  = Strophe.getText(priorityTag);
    }
    this.onPresence(info,msginfo);
};
connection.prototype.handlePing = function(e) {
    if(this.isClosed()){
        return;
    }
    var id = e.getAttribute('id');
    var from = e.getAttribute('from');
    var to = e.getAttribute('to');
    var dom = $iq({
                from : to,
                to : from,
                id : id,
                type : 'result'
    });
    this.sendCommand(dom.tree());
};
connection.prototype.handleIq = function(e) {
    var id = e.getAttribute('id');
    var from = e.getAttribute('from') || '';
    var name = parseNameFromJidFn(from);
    var curJid = this.context.jid;
    var curUser = this.context.userId;
    if (from !== "" && from != curJid && curUser != name)
        return true;

    var iqresult = $iq({type: 'result', id: id, from: curJid});
    this.sendCommand(iqresult.tree());

    var msgBodies = e.getElementsByTagName("query");
    if(msgBodies&&msgBodies.length>0){
        var queryTag = msgBodies[0];
        var rouster = parseFriendFn(queryTag);
        this.onRoster(rouster);
    }
    return true;
};
connection.prototype.handleMessage = function(msginfo){
    if(this.isClosed()){
        return;
    }
    var id = msginfo.getAttribute('id') || '';
    this.sendReceiptsMessage({
        id : id
    });
    var parseMsgData = parseResponseMessageFn(msginfo);
    if(parseMsgData.errorMsg){
        return;
    }
    var msgDatas = parseMsgData.data;
    for(var i in msgDatas){
        var msg = msgDatas[i];
        var from = msg.from;
        var too = msg.to;
        var extmsg = msg.ext || {};
        var chattype = msginfo.getAttribute('type') || 'chat';
        var msgBodies = msg.bodies;
        if(!msgBodies || msgBodies.length==0){
            continue;
        }
        var msgBody = msg.bodies[0];
        var type = msgBody.type;
        if ("txt" == type) {
            var receiveMsg = msgBody.msg;
            var emotionsbody = parseTextMessageFn(receiveMsg);
            if(emotionsbody.isemotion){
                this.onEmotionMessage({
                    id: id,
                    type : chattype,
                    from : from,
                    to : too,
                    data : emotionsbody.body,
                    ext : extmsg
                });
            } else {
                this.onTextMessage({
                    id: id,
                    type : chattype,
                    from : from,
                    to : too,
                    data : receiveMsg,
                    ext : extmsg
                });
            }
        } else if ("img" == type) {
            var rwidth = 0;
            var rheight = 0;
            if(msgBody.size){
                rwidth = msgBody.size.width;
                rheight = msgBody.size.height;
            }
            var msg = {
                id: id,
                type : chattype,
                from : from,
                to : too,
                url : msgBody.url,
                secret : msgBody.secret,
                filename : msgBody.filename,
                thumb : msgBody.thumb,
                thumb_secret : msgBody.thumb_secret,
                file_length : msgBody.file_length||'',
                width : rwidth,
                height : rheight,
                filetype : msgBody.filetype||'',
                accessToken : this.context.accessToken || '',
                ext : extmsg
            };
            this.onPictureMessage(msg);
        } else if ("audio" == type) {
            this.onAudioMessage({
                id: id,
                type : chattype,
                from : from,
                to : too,
                url : msgBody.url,
                secret : msgBody.secret,
                filename : msgBody.filename,
                length : msgBody.length||'',
                file_length : msgBody.file_length||'',
                filetype : msgBody.filetype||'',
                accessToken : this.context.accessToken || '',
                ext : extmsg
            });
        } else if ("file" == type) {
            this.onFileMessage({
                id: id,
                type : chattype,
                from : from,
                to : too,
                url : msgBody.url,
                secret : msgBody.secret,
                filename : msgBody.filename,
                file_length : msgBody.file_length,
                accessToken : this.context.accessToken || '',
                ext : extmsg
            });
        } else if ("loc" == type) {
            this.onLocationMessage({
                id: id,
                type : chattype,
                from : from,
                to : too,
                addr : msgBody.addr,
                lat : msgBody.lat,
                lng : msgBody.lng,
                ext : extmsg
            });
        }else if("video" == type){
            this.onVideoMessage({
                id: id,
                type : chattype,
                from : from,
                to : too,
                url : msgBody.url,
                secret : msgBody.secret,
                filename : msgBody.filename,
                file_length : msgBody.file_length,
                accessToken : this.context.accessToken || '',
                ext : extmsg
            });
        }else if("cmd" == type){
            this.onCmdMessage({
                id: id,
                from : from,
                to : too,
                action : msgBody.action,
                ext : extmsg
            });
        }
    }
};
connection.prototype.handleReceivedMessage = function(message){
    this.onReceivedMessage(message);
};
connection.prototype.handleInviteMessage = function(message){
    var form = null;
    var invitemsg = message.getElementsByTagName('invite');
    if(invitemsg && invitemsg.length>0){
        var fromJid = invitemsg[0].getAttribute('from');
        form = parseNameFromJidFn(fromJid);
    }
    var xmsg = message.getElementsByTagName('x');
    var roomid = null;
    if(xmsg && xmsg.length > 0){
        for(var i = 0; i < xmsg.length; i++){
            if('jabber:x:conference' == xmsg[i].namespaceURI){
                var roomjid = xmsg[i].getAttribute('jid');
                roomid = parseNameFromJidFn(roomjid);
            }
        }
    }
    this.onInviteMessage({
        type : 'invite',
        from : form,
        roomid : roomid
    });
};
connection.prototype.sendCommand = function(dom) {
    if(this.isOpened()){
        this.context.stropheConn.send(dom);
    } else {
        this.onError({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '连接还未建立,请先登录或等待登录处理完毕'
        });
    }
};
connection.prototype.getUniqueId = function (prefix)
{
    var cdate = new Date();
    var offdate = new Date(2010,1,1);
        var offset = cdate.getTime()-offdate.getTime();
        var hexd = parseInt(offset).toString(16);
    if (typeof(prefix) == "string" || typeof(prefix) == "number") {
        return prefix+"_"+hexd;
    } else {
        return 'WEBIM_'+hexd;
    }
};
connection.prototype.sendTextMessage = function(options) {
    var appKey = this.context.appKey || '';
    var toJid = appKey + "_" + options.to + "@" + this.domain;
    if(options.type && options.type == 'groupchat'){
        toJid = appKey + "_"+options.to+'@conference.' + this.domain;
    }
    if(options.resource){
        toJid = toJid + "/" + options.resource;
    }
    var msgTxt = options.msg;
    var json = {
        from : this.context.userId || '',
        to :    options.to,
        bodies : [{
            type : "txt",
            msg : msgTxt
        }],
        ext : options.ext || {}
    };
    var jsonstr = JSON.stringify(json);
    var dom = $msg({
        to : toJid,
        type : options.type || 'chat',
        id : this.getUniqueId(),
        xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.heartBeat = function(conn) {
    var options = {
        to : conn.domain,
        type : "normal"
    };
    conn.heartBeatID = setInterval(function() {
        conn.sendHeartBeatMessage(options);
    }, 60000);
}
connection.prototype.sendHeartBeatMessage = function(options) {
    var json = {};
    var jsonstr = JSON.stringify(json);
    var dom = $msg({
        to : options.to,
        type : options.type,
        id : this.getUniqueId(),
        xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.stopHeartBeat = function(conn) {
    clearInterval(conn.heartBeatID);
};
connection.prototype.sendPicture = function(options) {
    var onerror =  options.onFileUploadError || this.onError || emptyFn;
    var onFileUploadComplete = options.onFileUploadComplete || emptyFn;
    var conn = this;

    var myUploadComplete = function(data) {
        options["url"] = data.uri;
        options["secret"] = data.entities[0]["share-secret"];
        if(data.entities[0]["file-metadata"]){
            var file_len = data.entities[0]["file-metadata"]["content-length"];
            options["file_length"] = file_len;
            options["filetype"] = data.entities[0]["file-metadata"]["content-type"]
            if (file_len > 204800) {
                options["thumbnail"] = true;
            }
        }
        options["uuid"] = data.entities[0].uuid;

        onFileUploadComplete(data);
        conn.sendPictureMessage(options);
    };
    options.onFileUploadComplete = myUploadComplete;
    options.onFileUploadError = options.onFileUploadError|| this.onError || emptyFn;
    if(!isCanUploadFileAsync){
        options.appName = conn.context.appName || '';
        options.orgName = conn.context.orgName || '';
        options.accessToken = conn.context.accessToken || '';
        uploadFn.call(this, options);
        return;
    }
    
    var image = new Image();
    var imageLoadFn = function() {
        image.onload = null;
        if (!this.readyState || this.readyState == 'loaded'
                || this.readyState == 'complete') {
            var heigth = image.height;
            var width = image.width;
            options.height = heigth;
            options.width = width;
            options.appName = conn.context.appName || '';
            options.orgName = conn.context.orgName || '';
            options.accessToken = conn.context.accessToken || '';
            uploadFn(options);
        };
    };
    if("onload" in image){
        image.onload = imageLoadFn;
    } else {
        image.onreadystatechange = imageLoadFn;
    }
    image.onerror = function() {
        image.onerror = function(){
            image.onerror = null;
            options.onFileUploadError({
                type : EASEMOB_IM_UPLOADFILE_ERROR,
                msg : '指定的图片不存在或者不是一个图片格式文件'
            });
        };
        image.src = document.getElementById(options.fileInputId).value;
    };
    var picId = options.fileInputId;
    file = getFileUrlFn(picId);
    options.fileInfo = file;
    options.filename = file.filename;

    if (!file.url) {
        options.onFileUploadError({
            type : EASEMOB_IM_UPLOADFILE_NO_FILE,
            msg : '未选择上传文件'
        });
    } else {
        image.src = file.url;
    }
};
connection.prototype.sendPictureMessage = function(options) {
    var appKey = this.context.appKey || '';
    var toJid = appKey + "_" + options.to + "@" + this.domain;
    if(options.type && options.type == 'groupchat'){
        toJid = appKey + "_"+options.to+'@conference.' + this.domain;
    }
    if(options.resource){
        toJid = toJid + "/" + options.resource;
    }

    var json = {
                from : this.context.userId || '',
                to :    options.to,
                bodies :[{
                        type : "img",
                        url : options.url + '/' + options.uuid,
                        secret : options.secret,
                        filename : options.filename,
                        thumb : options.url + '/' + options.uuid,
                        thumb_secret : '',
                        size : {
                            width : options.width,
                            height : options.height
                        },
                        "file_length" : options.file_length,
                        filetype : options.filetype
                }],
                ext : options.ext || {}
    };
    var jsonstr = JSON.stringify(json);
    var date = new Date();
    var dom = $msg({
                type : options.type || 'chat',
                to : toJid,
                id : this.getUniqueId(),
                xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.sendAudio = function(options) {
    var onerror =  options.onFileUploadError || this.onError || emptyFn;
	var onFileUploadComplete = options.onFileUploadComplete || emptyFn;
    var conn = this;
	var myonComplete = function(data) {
		options["url"] = data.uri;
		options["secret"] = data.entities[0]["share-secret"];
		if(data.entities[0]["file-metadata"]){
			options["file_length"] = data.entities[0]["file-metadata"]["content-length"];
			options["filetype"] = data.entities[0]["file-metadata"]["content-type"];
		}
		options["uuid"] = data.entities[0].uuid;
		options["length"] = data.duration;
		onFileUploadComplete(data);
		conn.sendAudioMessage(options);
	};
	options.appName = this.context.appName || '';
	options.orgName = this.context.orgName || '';
	options.accessToken = this.context.accessToken || '';
	options.onFileUploadComplete = myonComplete;

	if(!isCanUploadFileAsync){
        uploadFn.call(this, options);
        return;
	}

    var file = getFileUrlFn(options.fileInputId);
    options.fileInfo = file;
    options.filename = file.filename;

    uploadFn(options, this);
};
connection.prototype.sendAudioMessage = function(options) {
    var appKey = this.context.appKey || '';
    var toJid = appKey + "_" + options.to + "@" + this.domain;
    if(options.type && options.type == 'groupchat'){
        toJid =appKey + "_"+options.to+'@conference.' + this.domain;
    }
    if(options.resource){
        toJid = toJid + "/" + options.resource;
    }

    var json = {
                from : this.context.userId || '',
                to :    options.to,
                bodies :[{
                        type : "audio",
                        url : options.url + '/' + options.uuid,
                        secret : options.secret,
                        filename : options.filename,
                        "file_length" : options.file_length,
                        length : options.length
                }],
                ext : options.ext || {}
    };
    var jsonstr = JSON.stringify(json);
    var dom = $msg({
                type : options.type || 'chat',
                to : toJid,
                id : this.getUniqueId(),
                xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.sendFileMessage = function(options) {
    var appKey = this.context.appKey || '';
    var toJid = appKey + "_" + options.to + "@" + this.domain;
    if(options.type && options.type == 'groupchat'){
        toJid =appKey + "_"+options.to+'@conference.' + this.domain;
    }

    if(options.resource){
        toJid = toJid + "/" + options.resource;
    }
    var json = {
                from : this.context.userId || '',
                to :    options.to,
                bodies :[{
                        type : "file",
                        url : options.url,
                        secret : options.secret,
                        filename : options.filename,
                        "file_length" : options.file_length
                }],
                ext : options.ext || {}
    };
    var jsonstr = JSON.stringify(json);
    var dom = $msg({
                type : 'chat',
                to : toJid,
                id : this.getUniqueId(),
                xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.sendLocationMessage = function(options) {
    var appKey = this.context.appKey || '';
    var toJid = appKey + "_" + options.to + "@" + this.domain;
    if(options.type && options.type == 'groupchat'){
        toJid =appKey + "_"+options.to+'@conference.' + this.domain;
    }

    if(options.resource){
        toJid = toJid + "/" + options.resource;
    }
    var json = {
                from : this.context.userId || '',
                to :    options.to,
                bodies :[{
                        type : "loc",
                        addr : options.addr,
                        lat : options.lat,
                        lng : options.lng
                }],
                ext : options.ext || {}
    };
    var jsonstr = JSON.stringify(json);
    var dom = $msg({
                type : 'chat',
                to : toJid,
                id : this.getUniqueId(),
                xmlns : "jabber:client"
    }).c("body").t(jsonstr);
    this.sendCommand(dom.tree());
};
connection.prototype.sendReceiptsMessage = function(options){
    var dom = $msg({
                from : this.context.jid || '',
                to : "easemob.com",
                id : options.id || ''
    }).c("received",{
                xmlns : "urn:xmpp:receipts",
                id : options.id || ''
            });
    this.sendCommand(dom.tree());
};
connection.prototype.addRoster = function(options){
    var jid = getJid(options,this);
    var name = options.name || '';
    var groups = options.groups || '';

    var iq = $iq({type : 'set'});
    iq.c("query",{xmlns:'jabber:iq:roster'});
    iq.c("item",{jid: jid ,name : name});

    if(groups){
        for (var i = 0; i < groups.length; i++){
            iq.c('group').t(groups[i]).up();
        }
    }
    var suc = options.success || emptyFn;
    var error = options.error || emptyFn;
    this.context.stropheConn.sendIQ(iq.tree(),suc,error);
};
connection.prototype.removeRoster = function(options){
    var jid = getJid(options,this);
    var iq = $iq({type: 'set'}).c('query', {xmlns : "jabber:iq:roster"}).c('item', {jid: jid,subscription: "remove"});

    var suc = options.success || emptyFn;
    var error = options.error || emptyFn;
    this.context.stropheConn.sendIQ(iq,suc,error);
};
connection.prototype.getRoster = function(options) {
    var conn = this;
    var dom  = $iq({
        type: 'get'
  }).c('query', {xmlns: 'jabber:iq:roster'});

    options = options || {};
    suc = options.success || this.onRoster;
  var completeFn = function(ele){
    var rouster = [];
        var msgBodies = ele.getElementsByTagName("query");
        if(msgBodies&&msgBodies.length>0){
            var queryTag = msgBodies[0];
            rouster = parseFriendFn(queryTag);
        }
    suc(rouster,ele);
  };
  error = options.error || this.onError;
  var failFn = function(ele){
        error({
            type : EASEMOB_IM_CONNCTION_GETROSTER_ERROR,
            msg : '获取联系人信息失败',
            data : ele
        });
  };
    if(this.isOpened()){
        this.context.stropheConn.sendIQ(dom.tree(),completeFn,failFn);
    } else {
        error({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '连接还未建立,请先登录或等待登录处理完毕'
        });
    }
};
connection.prototype.subscribe = function(options) {
    var jid = getJid(options,this);
    var pres = $pres({to: jid, type: "subscribe"});
    if (options.message) {
        pres.c("status").t(options.message).up();
    }
    if (options.nick) {
        pres.c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t(options.nick);
    }
    this.sendCommand(pres.tree());
};
connection.prototype.subscribed = function(options) {
    var jid = getJid(options,this);
    var pres = $pres({to : jid, type : "subscribed"});
    if (options.message) {
        pres.c("status").t(options.message).up();
    }
    this.sendCommand(pres.tree());
};
connection.prototype.unsubscribe = function(options) {
    var jid = getJid(options,this);
    var pres = $pres({to : jid, type : "unsubscribe"});
    if (options.message) {
        pres.c("status").t(options.message);
    }
    this.sendCommand(pres.tree());
};
connection.prototype.unsubscribed = function(options) {
    var jid = getJid(options,this);
    var pres = $pres({to : jid, type : "unsubscribed"});
    if (options.message) {
        pres.c("status").t(options.message).up();
    }
    this.sendCommand(pres.tree());
 };

connection.prototype.createRoom = function(options) {
    var suc =options.success || emptyFn;
    var err =  options.error || emptyFn;
    var roomiq;
    roomiq = $iq({
        to: options.rooomName,
        type: "set"
    }).c("query", {
        xmlns: Strophe.NS.MUC_OWNER
    }).c("x", {
         xmlns: "jabber:x:data",
         type: "submit"
    });
    return this.context.stropheConn.sendIQ(roomiq.tree(), suc, err);
};

connection.prototype.join = function(options){
    var roomJid = this.context.appKey+"_"+options.roomId+'@conference.' + this.domain;
    var room_nick = roomJid+"/"+this.context.userId;
    var suc =options.success || emptyFn;
    var err =  options.error || emptyFn;
    var errorFn = function (ele){
        err({
            type : EASEMOB_IM_CONNCTION_JOINROOM_ERROR,
            msg : '加入房间失败',
            data : ele
        });
    }
    var iq = $pres({
        from: this.context.jid,
        to: room_nick
    }).c("x", {
        xmlns: Strophe.NS.MUC
    });
    this.context.stropheConn.sendIQ(iq.tree(), suc, errorFn);
};
connection.prototype.listRooms = function(options) {
    var iq;
    iq = $iq({
      to: options.server||'conference.' + this.domain,
      from: this.context.jid,
      type: "get"
    }).c("query", {
      xmlns: Strophe.NS.DISCO_ITEMS
    });
    var suc =options.success || emptyFn;
    var completeFn = function(result){
        var rooms = [];
        rooms = parseRoomFn(result);
        suc(rooms);
    }
    var err =  options.error || emptyFn;
    var errorFn = function (ele){
        err({
            type : EASEMOB_IM_CONNCTION_GETROOM_ERROR,
            msg : '获取群组列表失败',
            data : ele
        });
    }
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomMember = function(options){
    var domain = this.domain;
    var members = [];
     var iq= $iq({
          to : this.context.appKey+"_"+options.roomId+'@conference.' + this.domain,
          type : 'get'
        }).c('query', {
            xmlns: Strophe.NS.MUC+'#admin'
        }).c('item',{
            affiliation:'member'
        });
    var suc =options.success || emptyFn;
    var completeFn = function(result){
        var items = result.getElementsByTagName('item');
        if(items){
            for(var i=0;i<items.length;i++){
                var item = items[i];
                var mem = {
                        jid : item.getAttribute('jid'),
                        affiliation : 'member'
                    };
                members.push(mem);
            }
        }
        suc(members);
    }
    var err =  options.error || emptyFn;
    var errorFn = function (ele){
        err({
            type : EASEMOB_IM_CONNCTION_GETROOMMEMBER_ERROR,
            msg : '获取群组成员列表失败',
            data : ele
        });
    }
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomInfo = function(options){
    var domain = this.domain;
    var iq= $iq({
          to:  this.context.appKey+"_"+options.roomId+'@conference.' + domain,
          type: "get"
        }).c("query", {
          xmlns: Strophe.NS.DISCO_INFO
        });
    var suc =options.success || emptyFn;
    var members = [];
    var completeFn = function(result){
        var fields = result.getElementsByTagName('field');
        if(fields){
            for(var i=0;i<fields.length;i++){
                var field = fields[i];
                if(field.getAttribute('label') == 'owner'){
                    var mem = {
                            jid : (field.textContent||field.text) + "@" + domain,
                            affiliation : 'owner'
                        };
                    members.push(mem);
                }
            }
        }
        suc(members);
    }
    var err =  options.error || emptyFn;
    var errorFn = function (ele){
        err({
            type : EASEMOB_IM_CONNCTION_GETROOMINFO_ERROR,
            msg : '获取群组信息失败',
            data : ele
        });
    }
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomOccupants = function(options) {
    var suc =options.success || emptyFn;
    var completeFn = function(result){
        var occupants = [];
        occupants = parseRoomOccupantsFn(result);
        suc(occupants);
    }
    var err =  options.error || emptyFn;
    var errorFn = function (ele){
        err({
            type : EASEMOB_IM_CONNCTION_GETROOMOCCUPANTS_ERROR,
            msg : '获取群组出席者列表失败',
            data : ele
        });
    }
    var attrs = {
      xmlns: Strophe.NS.DISCO_ITEMS
    };
    var info = $iq({
      from : this.context.jid,
      to : this.context.appKey+"_"+options.roomId+'@conference.' + this.domain,
      type : 'get'
    }).c('query', attrs);
    this.context.stropheConn.sendIQ(info.tree(), completeFn, errorFn);
  };

connection.prototype.setUserSig = function(desc) {
    var dom = $pres({xmlns : 'jabber:client'});
    desc = desc || "";
    dom.c("status").t(desc);
    this.sendCommand(dom.tree());
};
connection.prototype.setPresence = function(type,status) {
    var dom = $pres({xmlns : 'jabber:client'});
    if (type){
        if(status){
            dom.c("show").t(type);
            dom.up().c("status").t(status);
        } else {
            dom.c("show").t(type);
        }
    }
    this.sendCommand(dom.tree());
};
connection.prototype.getPresence = function() {
    var dom = $pres({xmlns : 'jabber:client'});
    var conn = this;
    this.sendCommand(dom.tree());
};
connection.prototype.ping = function(options) {
    options = options || {};
    var jid = getJid(options,this);

    var dom = $iq({
        from : this.context.jid || '',
        to: jid,
        type: "get"
    }).c("ping", {xmlns: "urn:xmpp:ping"});

    suc = options.success || emptyFn;
    error = options.error || this.onError;
    var failFn = function(ele){
        error({
            type : EASEMOB_IM_CONNCTION_PING_ERROR,
            msg : 'ping失败',
            data : ele
        });
    };
    if(this.isOpened()){
        this.context.stropheConn.sendIQ(dom.tree(),suc,failFn);
    } else {
        error({
            type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
            msg : '连接还未建立,请先登录或等待登录处理完毕'
        });
    }
    return;
};
connection.prototype.isOpened = function() {
    var status = this.context.status;
    return status==STATUS_OPENED;
};
connection.prototype.isOpening = function() {
    var status = this.context.status;
    return (status==STATUS_DOLOGIN_USERGRID) || (status==STATUS_DOLOGIN_IM);
};
connection.prototype.isClosing = function() {
    var status = this.context.status;
    return (status==STATUS_CLOSING);
};
connection.prototype.isClosed = function() {
    var status = this.context.status;
    return status == STATUS_CLOSED;
};
connection.prototype.clear = function() {
    this.context = {
        status : STATUS_INIT
    };
};


Easemob.im.Connection = connection;

Easemob.im.Helper = Easemob.im.Helper || {};

// method
Easemob.im.Helper.getFileUrl = getFileUrlFn;
Easemob.im.Helper.upload = uploadFn;
Easemob.im.Helper.download = downloadFn;
Easemob.im.Helper.getFileSize = getFileSizeFn;
Easemob.im.Helper.xhr = doAjaxRequest;
Easemob.im.Helper.parseTextMessage = parseTextMessageFn;
Easemob.im.Helper.login2UserGrid = login2UserGrid;
Easemob.im.Helper.parseUploadResponse = parseUploadResponse;
Easemob.im.Helper.parseDownloadResponse = parseDownloadResponse;
Easemob.im.Helper.getIEVersion = getIEVersion;

// attritue
Easemob.im.Helper.isCanUploadFile = isCanUploadFile;
Easemob.im.Helper.isCanUploadFileAsync = isCanUploadFileAsync;
Easemob.im.Helper.isCanDownLoadFile = isCanDownLoadFile;
Easemob.im.Helper.hasSetRequestHeader = hasSetRequestHeader;
Easemob.im.Helper.hasOverrideMimeType = hasOverrideMimeType;
Easemob.im.Helper.uploadUrl = '';

// object
Easemob.im.Helper.Base64 = innerBase64;
Easemob.im.Helper.EmotionPicData = emotionPicData;

//user
Easemob.im.Helper.registerUser = registerUserFn;

})(jQuery)
}
